//本质上就是对连续三个像素点进行中值滤波操作

module median_filter(
	clk			,
	rst_n			,
	data_in		,
	data_in_en	,
	hs_in			,
	vs_in			,
	data_out		,
	data_out_en	,
	hs_out		,
	vs_out		
);

	input clk;
	input rst_n;
	input [15:0] data_in;
	input data_in_en;
	input hs_in;
	input vs_in;
	output [15:0] data_out;
	output data_out_en;
	output hs_out;
	output vs_out;
	
	reg [15:0] mid;
	reg hs0,hs1,hs2;
	reg vs0,vs1,vs2;
	reg de0,de1,de2;
	reg [15:0] line0_data0,line0_data1,line0_data2;
	reg [15:0] line1_data0,line1_data1,line1_data2;
	reg [15:0] line2_data0,line2_data1,line2_data2;
	reg [15:0] line0_max,line0_mid,line0_min;
	reg [15:0] line1_max,line1_mid,line1_min;
	reg [15:0] line2_max,line2_mid,line2_min;
	reg [15:0] max_max,max_mid,max_min;
	reg [15:0] mid_max,mid_mid,mid_min;
	reg [15:0] min_max,min_mid,min_min;
	
	wire [15:0]line0;
	wire [15:0]line1;
	wire [15:0]line2;
	
	line3x3 line3x3(
		.clken		(data_in_en),
		.clock		(clk),
		.shiftin		(data_in),
		.shiftout	(),
		.taps0x		(line0),
		.taps1x		(line1),
		.taps2x		(line2)
	);
//生成3X3卷积核	
always @ (posedge clk or negedge rst_n)
	if (!rst_n)
		begin
			hs0 <= 0;
			hs1 <= 0;
			hs2 <= 0;
			vs0 <= 0;
			vs1 <= 0;
			vs2 <= 0;
			de0 <= 0;
			de1 <= 0;
			de2 <= 0;
		end
	else if (data_in_en)
		begin
			hs0 <= hs_in;
		   hs1 <= hs0;
		   hs2 <= hs1;
		   vs0 <= vs_in;
		   vs1 <= vs0;
		   vs2 <= vs1;
		   de0 <= data_in_en;
		   de1 <= de0;
		   de2 <= de1;
		end
		
always @ (posedge clk or negedge rst_n)
	if (!rst_n)
		begin
			line0_data0 <= 16'd0;
			line0_data1 <= 16'd0;
			line0_data2 <= 16'd0;
			line1_data0 <= 16'd0;
			line1_data1 <= 16'd0;
			line1_data2 <= 16'd0;
			line2_data0 <= 16'd0;
			line2_data1 <= 16'd0;
			line2_data2 <= 16'd0;
		end
	else if (data_in_en)     //3*3卷积核
		begin
			line0_data0 <= line0;
			line0_data1 <= line0_data0;
			line0_data2 <= line0_data1;
			line1_data0 <= line1;
			line1_data1 <= line1_data0;
			line1_data2 <= line1_data1;
			line2_data0 <= line2;
			line2_data1 <= line2_data0;
			line2_data2 <= line2_data1;
		end
		
always @ (posedge clk or negedge rst_n)
	if (!rst_n)
		begin
			line0_max <= 16'd0;
			line0_mid <= 16'd0;
			line0_min <= 16'd0;
		end
	else if (data_in_en)
		begin
			if ((line0_data0 >= line0_data1) && (line0_data0 >= line0_data2))  //判断line_data0为最大
				begin
					line0_max <= line0_data0;
					if (line0_data1 >= line0_data2)
						begin
							line0_mid <= line0_data1;
							line0_min <= line0_data2;
						end
					else
						begin
							line0_mid <= line0_data2;
							line0_min <= line0_data1;
						end
				end
			else if ((line0_data1 >= line0_data0) && (line0_data1 >= line0_data2))   //判断line_data1为最大
				begin
					line0_max <= line0_data1;
					if (line0_data0 >= line0_data2)
						begin
							line0_mid <= line0_data0;
							line0_min <= line0_data2;
						end
					else
						begin
							line0_mid <= line0_data2;
							line0_min <= line0_data0;
						end
				end
			else if ((line0_data2 >= line0_data0) && (line0_data2 >= line0_data1))   //判断line_data2为最大
				begin
					line0_max <= line0_data2;
					if (line0_data0 >= line0_data1)
						begin
							line0_mid <= line0_data0;
							line0_min <= line0_data1;
						end
					else
						begin
							line0_mid <= line0_data1;
							line0_min <= line0_data0;
						end
				end				
		end

always @ (posedge clk or negedge rst_n)
	if (!rst_n)
		begin
			line1_max <= 16'd0;
			line1_mid <= 16'd0;
			line1_min <= 16'd0;
		end
	else if (data_in_en)
		begin
			if ((line1_data0 >= line1_data1) && (line1_data0 >= line1_data2))//第二行做最大值、中值、最小值排序
				begin
					line1_max <= line1_data0;
					if (line1_data1 >= line1_data2)
						begin
							line1_mid <= line1_data1;
							line1_min <= line1_data2;
						end
					else
						begin
							line1_mid <= line1_data2;
							line1_min <= line1_data1;
						end
				end
			else if ((line1_data1 >= line1_data0) && (line1_data1 >= line1_data2))
				begin
					line1_max <= line1_data1;
					if (line1_data0 >= line1_data2)
						begin
							line1_mid <= line1_data0;
							line1_min <= line1_data2;
						end
					else
						begin
							line1_mid <= line1_data2;
							line1_min <= line1_data0;
						end
				end
			else if ((line1_data2 >= line1_data0) && (line1_data2 >= line1_data1))
				begin
					line1_max <= line1_data2;
					if (line1_data0 >= line1_data1)
						begin
							line1_mid <= line1_data0;
							line1_min <= line1_data1;
						end
					else
						begin
							line1_mid <= line1_data1;
							line1_min <= line1_data0;
						end
				end				
		end
		
always @ (posedge clk or negedge rst_n)
	if (!rst_n)
		begin
			line2_max <= 16'd0;
			line2_mid <= 16'd0;
			line2_min <= 16'd0;
		end
	else if (data_in_en)
		begin
			if ((line2_data0 >= line2_data1) && (line2_data0 >= line2_data2))  //第三行做最大值、中值、最小值排序
				begin
					line2_max <= line2_data0;
					if (line2_data1 >= line2_data2)
						begin
							line2_mid <= line2_data1;
							line2_min <= line2_data2;
						end
					else
						begin
							line2_mid <= line2_data2;
							line2_min <= line2_data1;
						end
				end
			else if ((line2_data1 >= line2_data0) && (line2_data1 >= line2_data2))
				begin
					line2_max <= line2_data1;
					if (line2_data0 >= line2_data2)
						begin
							line2_mid <= line2_data0;
							line2_min <= line2_data2;
						end
					else
						begin
							line2_mid <= line2_data2;
							line2_min <= line2_data0;
						end
				end
			else if ((line2_data2 >= line2_data0) && (line2_data2 >= line2_data1))
				begin
					line2_max <= line2_data2;
					if (line2_data0 >= line2_data1)
						begin
							line2_mid <= line2_data0;
							line2_min <= line2_data1;
						end
					else
						begin
							line2_mid <= line2_data1;
							line2_min <= line2_data0;
						end
				end				
		end
		
always @ (posedge clk or negedge rst_n)
	if (!rst_n)
		begin
			max_max <= 16'd0;
			max_mid <= 16'd0;
			max_min <= 16'd0;
		end
	else if (data_in_en)
		begin
			if ((line0_max >= line1_max) && (line0_max >= line2_max))  //跨行做最大、中值、最小值排序
				begin
					max_max <= line0_max;
					if (line1_max >= line2_max)
						begin
							max_mid <= line1_max;
							max_min <= line2_max;
						end
					else
						begin
							max_mid <= line2_max;
							max_min <= line1_max;
						end
				end
			else if ((line1_max >= line0_max) && (line1_max >= line2_max))
				begin
					max_max <= line1_max;
					if (line0_max >= line2_max)
						begin
							max_mid <= line0_max;
							max_min <= line2_max;
						end
					else
						begin
							max_mid <= line2_max;
							max_min <= line0_max;
						end
				end
			else if ((line2_max >= line0_max) && (line2_max >= line1_max))
				begin
					max_max <= line2_max;
					if (line0_max >= line1_max)
						begin
							max_mid <= line0_max;
							max_min <= line1_max;
						end
					else
						begin
							max_mid <= line1_max;
							max_min <= line0_max;
						end
				end			
		end
		
always @ (posedge clk or negedge rst_n)
	if (!rst_n)
		begin
			mid_max <= 16'd0;
			mid_mid <= 16'd0;
			mid_min <= 16'd0;
		end
	else if (data_in_en)
		begin
			if ((line0_mid >= line1_mid) && (line0_mid >= line2_mid))
				begin
					mid_max <= line0_mid;
					if (line1_mid >= line2_mid)
						begin
							mid_mid <= line1_mid;
							mid_min <= line2_mid;
						end
					else
						begin
							mid_mid <= line2_mid;
							mid_min <= line1_mid;
						end
				end
			else if ((line1_mid >= line0_mid) && (line1_mid >= line2_mid))
				begin
					mid_max <= line1_mid;
					if (line0_mid >= line2_mid)
						begin
							mid_mid <= line0_mid;
							mid_min <= line2_mid;
						end
					else
						begin
							mid_mid <= line2_mid;
							mid_min <= line0_mid;
						end
				end
			else if ((line2_mid >= line0_mid) && (line2_mid >= line1_mid))
				begin
					mid_max <= line2_mid;
					if (line0_mid >= line1_mid)
						begin
							mid_mid <= line0_mid;
							mid_min <= line1_mid;
						end
					else
						begin
							mid_mid <= line1_mid;
							mid_min <= line0_mid;
						end
				end			
		end		
		
always @ (posedge clk or negedge rst_n)
	if (!rst_n)
		begin
			min_max <= 16'd0;
			min_mid <= 16'd0;
			min_min <= 16'd0;
		end
	else if (data_in_en)
		begin
			if ((line0_min >= line1_min) && (line0_min >= line2_min))
				begin
					min_max <= line0_min;
					if (line1_min >= line2_min)
						begin
							min_mid <= line1_min;
							min_min <= line2_min;
						end
					else
						begin
							min_mid <= line2_min;
							min_min <= line1_min;
						end
				end
			else if ((line1_min >= line0_min) && (line1_min >= line2_min))
				begin
					min_max <= line1_min;
					if (line0_min >= line2_min)
						begin
							min_mid <= line0_min;
							min_min <= line2_min;
						end
					else
						begin
							min_mid <= line2_min;
							min_min <= line0_min;
						end
				end
			else if ((line2_min >= line0_min) && (line2_min >= line1_min))
				begin
					min_max <= line2_min;
					if (line0_min >= line1_min)
						begin
							min_mid <= line0_min;
							min_min <= line1_min;
						end
					else
						begin
							min_mid <= line1_min;
							min_min <= line0_min;
						end
				end			
		end

always @(posedge clk or negedge rst_n)
	if(!rst_n)
		mid <= 16'd0;
	else if(data_in_en) 
		begin
			if(((max_mid >= mid_mid) && (max_mid < min_mid)) || ((max_mid >= min_mid) && (max_mid < mid_mid)))
				mid <= max_mid;
			else if(((mid_mid > max_mid) && (mid_mid < min_mid)) || ((min_mid >= min_mid) && (mid_mid < max_mid)))
				mid <= mid_mid;
			else if(((min_mid > max_mid) && (min_mid < mid_mid)) || ((min_mid > mid_mid) && (mid_min < max_mid)))
				mid <= min_mid;
		end

	assign data_out    = mid;
	assign data_out_en = de2;  //打三拍 因为要计算相邻三个像素的中值
	assign hs_out      = hs2;
	assign vs_out      = vs2;

endmodule 